package services

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"time"

	"cashew.com/cashew_api/models"
	"cashew.com/cashew_common/configs"
	"cashew.com/cashew_common/constants"
	"github.com/beego/beego/v2/core/logs"
)

type AnchorInfo struct {
	AnchorId    int64
	Status      int
	SubRegionId int
}

type AnchorMatchPoolUserInfo struct {
	UserId      int64
	SubRegionId int
	CreateTime  int64
	BlackList   []int64
}

// 判断是否是用户近期已经匹配过的主播(当前轮次)
func hasAnchorMatchedByUser(userId, anchorId int64) (hasMatched bool) {
	matchedKeyPrefix := configs.TickerTaskMatchedKeyPrefix
	matchedKey := matchedKeyPrefix + fmt.Sprintf("%d", userId)
	return Redisserver.SIsMember(matchedKey, fmt.Sprintf("%d", anchorId))
}

// 把用户匹配到的主播存入 redis 集合
func AddUserMatchedAnchor(userId int64, anchorId int64) (err error) {
	matchedKeyPrefix := configs.TickerTaskMatchedKeyPrefix
	matchedAnchorListKey := matchedKeyPrefix + fmt.Sprintf("%d", userId)
	_ = Redisserver.SAdd(matchedAnchorListKey, fmt.Sprintf("%d", anchorId))
	Redisserver.Expire(matchedAnchorListKey, 1*time.Hour)
	return nil
}

// 把用户匹配到的主播存入 redis 集合
func ClearUserMatchedAnchor(userId int64) {
	matchedKeyPrefix := configs.TickerTaskMatchedKeyPrefix
	matchedAnchorListKey := matchedKeyPrefix + fmt.Sprintf("%d", userId)
	_ = Redisserver.Del(matchedAnchorListKey)
	return
}

// 重置用户匹配到的主播集合过期时间
func ResetUserMatchedAnchorListExpireTime(userId int64) (err error) {
	matchedKeyPrefix := configs.TickerTaskMatchedKeyPrefix
	matchedAnchorListKey := matchedKeyPrefix + fmt.Sprintf("%d", userId)
	Redisserver.Expire(matchedAnchorListKey, 1*time.Hour)
	return nil
}

func SubmitUserToAnchorMatchPool(userId int64) (err error) {
	userPoolKey := configs.TickerTaskUserPoolKey
	userInfoKeyPrefix := configs.TickerTaskUserInfoKeyPrefix
	userInfoKey := userInfoKeyPrefix + fmt.Sprintf("%d", userId)
	userInfo, err := models.GetUsersByUserId(userId)
	if err != nil {
		return err
	}
	subRegionId := userInfo.SubRegionId

	blackList, err := models.GetBlackAnchorIdsByUserId(userId)
	if err != nil {
		return err
	}
	blackListStr, err := json.Marshal(blackList)
	if err != nil {
		return err
	}

	// 加入用户请求hash表
	Redisserver.HSet(userInfoKey, "user_id", fmt.Sprintf("%d", userId))
	Redisserver.HSet(userInfoKey, "sub_region_id", fmt.Sprintf("%d", subRegionId))
	Redisserver.HSet(userInfoKey, "black_list", string(blackListStr))
	Redisserver.Expire(userInfoKey, 24*time.Hour)

	// 加入用户请求池
	existsInZSet := Redisserver.ExistsInZSet(userPoolKey, fmt.Sprintf("%d", userId))
	if !existsInZSet {
		createTime := time.Now().Unix()
		err = Redisserver.ZAdd(userPoolKey, float64(createTime), fmt.Sprintf("%d", userId))
		if err != nil {
			return err
		}
	}

	return nil
}

// 判断用户是否在用户池中
func IsUserInUserPool(userId int64) (isInPool bool) {
	userPoolKey := configs.TickerTaskUserPoolKey
	existsInZSet := Redisserver.ExistsInZSet(userPoolKey, fmt.Sprintf("%d", userId))
	return existsInZSet
}

// 把用户从用户池中移除
func RemoveUserFromUserPool(userIdstr string) (err error) {
	userPoolKey := configs.TickerTaskUserPoolKey
	_ = Redisserver.ZRem(userPoolKey, userIdstr)

	// 释放用户锁
	_ = Redisserver.ReleaseCallUserLock(userIdstr)

	return nil
}

// 匹配成功之后的redis操作
func AfterMatchAnchor(userId int64, anchorId int64) (err error) {
	// 1. 加入用户匹配到的主播集合
	err = AddUserMatchedAnchor(userId, anchorId)
	if err != nil {
		return err
	}

	return nil
}

// 快速拨打电话，给用户随机分配一个主播逻辑
func QuickCallMatchLogic(userId int64) (anchorMatched *AnchorInfo, errNo constants.ErrNo) {
	UserIdStr := fmt.Sprintf("%d", userId)
	// 检查如果有匹配任务，则删除任务，避免重复匹配
	_ = RemoveUserFromUserPool(UserIdStr)
	// 给用户自己加锁，防止中途有主播打给自己
	matchedKeyExpire := configs.TickerTaskMatchedKeyExpire
	timerQuickCallSeconds := configs.TickerTaskTimerQuickCallSeconds
	ReSetCallUserExpire := int(matchedKeyExpire + timerQuickCallSeconds)
	_ = Redisserver.ReSetCallUserLock(UserIdStr, ReSetCallUserExpire)

	//// 判断用户余额是否小于最低价格要求 (暂不需要)
	//minCoinForQuickCall := configs.TickerTaskMinCoinForQuickCall

	// 查询用户基本信息
	userInfo, err := models.GetUsersByUserId(userId)
	if err != nil {
		return nil, constants.ERR_USER_NOT_EXIST
	}

	userSubRegionId := userInfo.SubRegionId

	// 1. 查询用户同区域，不在黑名单中的，在线主播列表
	anchorList, err := GetOnlineTakingOrderAnchorsByUserId(userId, 1, userSubRegionId)
	if err != nil {
		return nil, constants.ERR_SYS
	}

	// 执行匹配算法
	anchorMatched, err = MatchAnchor(userId, userSubRegionId, anchorList, 1)
	if err != nil {
		return nil, constants.ERR_SYS
	}
	if anchorMatched == nil {
		// 同区域里，没有匹配到可用的主播
		logs.Info("快速拨打电话接口速配,userId:%d,同区域里，没有匹配到可用的主播", userId)

		// 2. 继续在其他分区匹配
		anchorList, err = GetOnlineTakingOrderAnchorsByUserId(userId, 2, userSubRegionId)
		if err != nil {
			return nil, constants.ERR_SYS
		}
		anchorMatched, err = MatchAnchor(userId, userSubRegionId, anchorList, 2)
		if err != nil {
			return nil, constants.ERR_SYS
		}
	}

	if anchorMatched == nil {
		// 其他分区里，没有匹配到可用的主播
		logs.Info("快速拨打电话接口速配，userId:%d,其他分区里，没有匹配到可用的主播", userId)
		return nil, constants.SUCCESS
	}

	return anchorMatched, constants.SUCCESS
}

// 根据主播列表，执行匹配算法，返回匹配到的主播 matchType: 0: 全部匹配 1: 同区域匹配 2：匹配不在同一区域的主播
func MatchAnchor(userId int64, userSubRegionId int, anchorIdList []AnchorInfo, matchType int) (anchorMatched *AnchorInfo, err error) {
	if len(anchorIdList) == 0 {
		// 没有可匹配的主播
		return nil, nil
	}
	// 打乱切片顺序
	rand.Shuffle(len(anchorIdList), func(i, j int) {
		anchorIdList[i], anchorIdList[j] = anchorIdList[j], anchorIdList[i]
	})

	// 开始第一轮匹配：同区域，在线接单用户，近期未匹配过的用户
	isMatch := false
	clearMatched := false
	// 1. 匹配类型：全匹配 或 同区域
	if matchType == 0 || matchType == 1 {
		for _, anchor := range anchorIdList {
			if anchor.SubRegionId == userSubRegionId && anchor.Status == constants.ANCHOR_ONLINETAKINGORDERS {
				// 判断近期是否匹配过
				hasrMatched := hasAnchorMatchedByUser(userId, anchor.AnchorId)
				if !hasrMatched {
					// 获取匹配锁,能获得锁才是真的匹配成功
					anchorIdStr := fmt.Sprintf("%d", anchor.AnchorId)
					getLock, _ := Redisserver.AcquireCallUserLock(anchorIdStr, 30)
					if getLock {
						anchorMatched = &anchor
						isMatch = true
						break
					}
				}

			}
		}
		if !isMatch {
			// 如果第一轮匹配失败，继续匹配第二轮：同区域，在线空闲用户，最近未匹配过的
			for _, anchor := range anchorIdList {
				if anchor.SubRegionId == userSubRegionId && anchor.Status == constants.ANCHOR_ONLINEIDLE {
					// 判断近期是否匹配过
					hasrMatched := hasAnchorMatchedByUser(userId, anchor.AnchorId)
					if !hasrMatched {
						// 获取匹配锁,能获得锁才是真的匹配成功
						anchorIdStr := fmt.Sprintf("%d", anchor.AnchorId)
						getLock, _ := Redisserver.AcquireCallUserLock(anchorIdStr, 30)
						if getLock {
							anchorMatched = &anchor
							isMatch = true
							break
						}
					}

				}
			}
		}
		if !isMatch {
			// 如果第二轮匹配失败，继续匹配第三轮：同区域，在线接单，最近已经匹配过的
			for _, anchor := range anchorIdList {
				if anchor.SubRegionId == userSubRegionId && anchor.Status == constants.ANCHOR_ONLINETAKINGORDERS {
					// 判断是近期配过的
					hasrMatched := hasAnchorMatchedByUser(userId, anchor.AnchorId)
					if hasrMatched {
						// 获取匹配锁,能获得锁才是真的匹配成功
						anchorIdStr := fmt.Sprintf("%d", anchor.AnchorId)
						getLock, _ := Redisserver.AcquireCallUserLock(anchorIdStr, 30)
						if getLock {
							anchorMatched = &anchor
							clearMatched = true
							isMatch = true
							break
						}
					}
				}
			}
		}
		if !isMatch {
			// 如果第三轮匹配失败，继续匹配第四轮：同区域，在线空闲，最近已经匹配过的
			for _, anchor := range anchorIdList {
				if anchor.SubRegionId == userSubRegionId && anchor.Status == constants.ANCHOR_ONLINEIDLE {
					// 判断是近期配过的
					hasrMatched := hasAnchorMatchedByUser(userId, anchor.AnchorId)
					if hasrMatched {
						// 获取匹配锁,能获得锁才是真的匹配成功
						anchorIdStr := fmt.Sprintf("%d", anchor.AnchorId)
						getLock, _ := Redisserver.AcquireCallUserLock(anchorIdStr, 30)
						if getLock {
							anchorMatched = &anchor
							clearMatched = true
							isMatch = true
							break
						}
					}
				}
			}
		}
	}

	// 2. 匹配类型：全匹配 或 非同区域
	if matchType == 0 || matchType == 2 {
		if !isMatch {
			// 如果第四轮匹配失败，继续在非同区域外进行匹配(第五轮)，匹配优先级：非同区域，在线接单，最近未匹配过的
			for _, anchor := range anchorIdList {
				if anchor.SubRegionId != userSubRegionId && anchor.Status == constants.ANCHOR_ONLINETAKINGORDERS {
					// 判断近期是否匹配过
					hasrMatched := hasAnchorMatchedByUser(userId, anchor.AnchorId)
					if !hasrMatched {
						// 获取匹配锁,能获得锁才是真的匹配成功
						anchorIdStr := fmt.Sprintf("%d", anchor.AnchorId)
						getLock, _ := Redisserver.AcquireCallUserLock(anchorIdStr, 30)
						if getLock {
							anchorMatched = &anchor
							isMatch = true
							break
						}
					}
				}
			}
		}
		if !isMatch {
			// 如果第五轮匹配失败，继续在非同区域外进行匹配(第六轮)，匹配优先级：非同区域，在线空闲，最近未匹配过的
			for _, anchor := range anchorIdList {
				if anchor.SubRegionId != userSubRegionId && anchor.Status == constants.ANCHOR_ONLINEIDLE {
					// 判断近期是否匹配过
					hasrMatched := hasAnchorMatchedByUser(userId, anchor.AnchorId)
					if !hasrMatched {
						// 获取匹配锁,能获得锁才是真的匹配成功
						anchorIdStr := fmt.Sprintf("%d", anchor.AnchorId)
						getLock, _ := Redisserver.AcquireCallUserLock(anchorIdStr, 30)
						if getLock {
							anchorMatched = &anchor
							isMatch = true
							break
						}
					}
				}
			}
		}
		if !isMatch {
			// 如果第六轮匹配失败，继续在非同区域外进行匹配(第七轮)，匹配优先级：非同区域，在线接单，最近匹配过的
			for _, anchor := range anchorIdList {
				if anchor.SubRegionId != userSubRegionId && anchor.Status == constants.ANCHOR_ONLINETAKINGORDERS {
					// 判断近期是否匹配过
					hasrMatched := hasAnchorMatchedByUser(userId, anchor.AnchorId)
					if hasrMatched {
						// 获取匹配锁,能获得锁才是真的匹配成功
						anchorIdStr := fmt.Sprintf("%d", anchor.AnchorId)
						getLock, _ := Redisserver.AcquireCallUserLock(anchorIdStr, 30)
						if getLock {
							anchorMatched = &anchor
							clearMatched = true
							isMatch = true
							break
						}
					}
				}
			}
		}
		if !isMatch {
			// 如果第七轮匹配失败，继续在非同区域外进行匹配(第八轮)，匹配优先级：非同区域，在线空闲，最近匹配过的
			for _, anchor := range anchorIdList {
				if anchor.SubRegionId != userSubRegionId && anchor.Status == constants.ANCHOR_ONLINEIDLE {
					// 判断近期是否匹配过
					hasrMatched := hasAnchorMatchedByUser(userId, anchor.AnchorId)
					if hasrMatched {
						// 获取匹配锁,能获得锁才是真的匹配成功
						anchorIdStr := fmt.Sprintf("%d", anchor.AnchorId)
						getLock, _ := Redisserver.AcquireCallUserLock(anchorIdStr, 30)
						if getLock {
							anchorMatched = &anchor
							clearMatched = true
							isMatch = true
							break
						}
					}
				}
			}
		}
	}

	if clearMatched {
		// 清除匹配过的主播
		ClearUserMatchedAnchor(userId)
	}

	return
}

// 快速拨打电话，给匹配到的主播和用户发要求通知
func QuickCallNotice(userId int64, anchorId int64) (errNo constants.ErrNo) {
	anchorIdStr := fmt.Sprintf("%d", anchorId)

	// 判断至少有1分钟使用的金币余额
	//isChecked, payPrice, anchorPrice, err := CheckMaleCallCost(userId, anchorId)
	checkRes, err := CheckMaleCallCost(userId, anchorId)
	if err != nil {
		logs.Error("CheckMaleCallCost(%v, %v): %v", userId, anchorId, err)
		return constants.ERR_SYS
	}
	if !checkRes.Checked {
		return constants.ERR_USER_LOW_BALANCE
	}
	payPrice := checkRes.PayPrice
	anchorPrice := checkRes.AnchorPrice

	// 免费卡信息，用户是否是免费卡通话，免费通话卡时间
	isFreeCard := false
	freeCallNum, freeCallCardTime, err := CheckFreeCallCard(userId)
	if err != nil {
		logs.Error("CheckFreeCallCard(%v): %v", userId, err)
		return constants.ERR_SYS
	}
	if freeCallNum > 0 {
		isFreeCard = true
	}

	// 查询用户信息详情【读缓存】
	strAnchorId := fmt.Sprintf("%d", anchorId)
	anchorCacheInfo, err := GetUserInfoFromRedis(Redisserver, strAnchorId, constants.IDENTITY_ANCHOR)
	if err != nil {
		logs.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		return constants.ERR_USER_NOT_EXIST
	}
	strViewerId := fmt.Sprintf("%d", userId)
	viewerCacheInfo, err := GetUserInfoFromRedis(Redisserver, strViewerId, constants.IDENTITY_NORMAL_USER)
	if err != nil {
		logs.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		return constants.ERR_USER_NOT_EXIST
	}

	// 发送IM消息1 通知对方
	noticeUserId := fmt.Sprintf("%d", anchorId)
	reqIm := AssembleImVideocallQuickCallRequest(noticeUserId, payPrice, anchorPrice, anchorId, isFreeCard, freeCallCardTime, viewerCacheInfo, anchorCacheInfo)
	err = ImVideocallCall(&reqIm)
	if err != nil {
		logs.Error("IM发布请求失败：videocallCall(%+v): %v", reqIm, err)
		return constants.ERR_IM_SENDM_FAIL
	}

	// 发送IM消息2 通知自己
	noticeUserId = fmt.Sprintf("%d", userId) // 通知自己
	reqIm2 := AssembleImVideocallQuickCallRequest(noticeUserId, payPrice, anchorPrice, anchorId, isFreeCard, freeCallCardTime, viewerCacheInfo, anchorCacheInfo)
	err = ImVideocallCall(&reqIm2)
	if err != nil {
		logs.Error("IM发布请求失败：videocallCall(%+v): %v", reqIm2, err)
		return constants.ERR_IM_SENDM_FAIL
	}
	_ = Redisserver.ReleaseCallUserLock(anchorIdStr)
	return constants.SUCCESS
}
